//! Assertion builders and utilities
//! 
//! This module provides convenient builders for creating common assertions
//! and validation configurations.

use super::{
    validators::*,
    extractors::*,
    ValidationEngine,
    Validator,
    Extractor,
};
use crate::error::Result;
use crate::models::{Value, ValueMap};
use serde_json::json;
use std::collections::HashMap;

/// Assertion builder for creating validation configurations
pub struct AssertionBuilder {
    validators: Vec<Box<dyn Validator>>,
    extractors: Vec<Box<dyn Extractor>>,
}

impl AssertionBuilder {
    /// Create new assertion builder
    pub fn new() -> Self {
        Self {
            validators: Vec::new(),
            extractors: Vec::new(),
        }
    }
    
    /// Assert status code equals expected value
    pub fn status_code(mut self, expected: u16) -> Self {
        self.validators.push(Box::new(StatusCodeValidator::new(expected)));
        self
    }
    
    /// Assert that a JSON path equals expected value
    pub fn json_path_equals(mut self, path: &str, expected: Value) -> Self {
        self.validators.push(Box::new(EqualsValidator::new(path.to_string(), expected)));
        self
    }
    
    /// Assert that a JSON path contains expected value
    pub fn json_path_contains(mut self, path: &str, expected: Value) -> Self {
        self.validators.push(Box::new(ContainsValidator::new(path.to_string(), expected)));
        self
    }
    
    /// Assert that a JSON path has expected length
    pub fn json_path_length(mut self, path: &str, expected_length: usize) -> Self {
        self.validators.push(Box::new(LengthValidator::new(path.to_string(), expected_length)));
        self
    }
    
    /// Assert that a JSON path matches regex pattern
    pub fn json_path_regex(mut self, path: &str, pattern: &str) -> Result<Self> {
        let validator = RegexValidator::new(path.to_string(), pattern)?;
        self.validators.push(Box::new(validator));
        Ok(self)
    }
    
    /// Assert that a JSON path has expected type
    pub fn json_path_type(mut self, path: &str, expected_type: &str) -> Self {
        self.validators.push(Box::new(TypeValidator::new(path.to_string(), expected_type.to_string())));
        self
    }
    
    /// Extract value from JSON path
    pub fn extract_json_path(mut self, path: &str, variable_name: &str) -> Self {
        self.extractors.push(Box::new(JsonPathExtractor::new(path.to_string(), variable_name.to_string())));
        self
    }
    
    /// Extract value using regex
    pub fn extract_regex(mut self, path: &str, pattern: &str, variable_name: &str, group_index: Option<usize>) -> Result<Self> {
        let extractor = RegexExtractor::new(path.to_string(), pattern, variable_name.to_string(), group_index)?;
        self.extractors.push(Box::new(extractor));
        Ok(self)
    }
    
    /// Extract header value
    pub fn extract_header(mut self, header_name: &str, variable_name: &str) -> Self {
        self.extractors.push(Box::new(HeaderExtractor::new(header_name.to_string(), variable_name.to_string())));
        self
    }
    
    /// Extract cookie value
    pub fn extract_cookie(mut self, cookie_name: &str, variable_name: &str) -> Self {
        self.extractors.push(Box::new(CookieExtractor::new(cookie_name.to_string(), variable_name.to_string())));
        self
    }
    
    /// Extract status code
    pub fn extract_status_code(mut self, variable_name: &str) -> Self {
        self.extractors.push(Box::new(StatusCodeExtractor::new(variable_name.to_string())));
        self
    }
    
    /// Extract response time
    pub fn extract_response_time(mut self, variable_name: &str) -> Self {
        self.extractors.push(Box::new(ResponseTimeExtractor::new(variable_name.to_string())));
        self
    }
    
    /// Extract content length
    pub fn extract_content_length(mut self, variable_name: &str) -> Self {
        self.extractors.push(Box::new(ContentLengthExtractor::new(variable_name.to_string())));
        self
    }
    
    /// Build validation configuration
    pub fn build(self) -> (ValueMap, ValueMap) {
        let mut validators = HashMap::new();
        let mut extractors = HashMap::new();
        
        // Convert validators to configuration
        for (i, validator) in self.validators.into_iter().enumerate() {
            let key = format!("{}_{}", validator.name(), i);
            validators.insert(key, json!({})); // Placeholder config
        }
        
        // Convert extractors to configuration
        for (i, extractor) in self.extractors.into_iter().enumerate() {
            let key = format!("{}_{}", extractor.name(), i);
            extractors.insert(key, json!({
                "variable_name": extractor.variable_name()
            }));
        }
        
        (validators, extractors)
    }
}

impl Default for AssertionBuilder {
    fn default() -> Self {
        Self::new()
    }
}

/// Common assertion patterns
pub struct CommonAssertions;

impl CommonAssertions {
    /// Assert successful HTTP response (2xx status code)
    pub fn success() -> AssertionBuilder {
        AssertionBuilder::new()
            .json_path_type("status_code", "integer")
    }
    
    /// Assert HTTP 200 OK response
    pub fn ok() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(200)
    }
    
    /// Assert HTTP 201 Created response
    pub fn created() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(201)
    }
    
    /// Assert HTTP 400 Bad Request response
    pub fn bad_request() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(400)
    }
    
    /// Assert HTTP 401 Unauthorized response
    pub fn unauthorized() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(401)
    }
    
    /// Assert HTTP 403 Forbidden response
    pub fn forbidden() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(403)
    }
    
    /// Assert HTTP 404 Not Found response
    pub fn not_found() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(404)
    }
    
    /// Assert HTTP 500 Internal Server Error response
    pub fn server_error() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(500)
    }
    
    /// Assert JSON response with specific structure
    pub fn json_response() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(200)
            .json_path_type("body", "object")
    }
    
    /// Assert paginated response structure
    pub fn paginated_response() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(200)
            .json_path_type("body.data", "array")
            .json_path_type("body.total", "integer")
            .json_path_type("body.page", "integer")
            .json_path_type("body.per_page", "integer")
    }
    
    /// Assert API error response structure
    pub fn error_response() -> AssertionBuilder {
        AssertionBuilder::new()
            .json_path_type("body.error", "object")
            .json_path_type("body.error.code", "string")
            .json_path_type("body.error.message", "string")
    }
    
    /// Assert authentication response with token
    pub fn auth_response() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(200)
            .json_path_type("body.token", "string")
            .json_path_type("body.expires_in", "integer")
            .extract_json_path("body.token", "auth_token")
            .extract_json_path("body.expires_in", "token_expires_in")
    }
    
    /// Assert user profile response
    pub fn user_profile() -> AssertionBuilder {
        AssertionBuilder::new()
            .status_code(200)
            .json_path_type("body.id", "integer")
            .json_path_type("body.email", "string")
            .json_path_type("body.name", "string")
            .extract_json_path("body.id", "user_id")
            .extract_json_path("body.email", "user_email")
    }
    
    /// Assert list response with items
    pub fn list_response(min_items: Option<usize>) -> AssertionBuilder {
        let mut builder = AssertionBuilder::new()
            .status_code(200)
            .json_path_type("body", "array");
        
        if let Some(min) = min_items {
            // Note: This would need a "minimum length" validator
            // For now, we'll just check that it's an array
        }
        
        builder
    }
    
    /// Assert response time is within acceptable range
    pub fn fast_response(max_ms: i64) -> AssertionBuilder {
        AssertionBuilder::new()
            .extract_response_time("response_time")
        // Note: Would need a "less than" validator to check max_ms
    }
}

/// Validation configuration builder
pub struct ValidationConfigBuilder {
    engine: ValidationEngine,
}

impl ValidationConfigBuilder {
    /// Create new validation config builder
    pub fn new() -> Self {
        Self {
            engine: ValidationEngine::new(),
        }
    }
    
    /// Add custom validator
    pub fn add_validator(mut self, name: String, validator: Box<dyn Validator>) -> Self {
        self.engine.register_validator(name, validator);
        self
    }
    
    /// Add custom extractor
    pub fn add_extractor(mut self, name: String, extractor: Box<dyn Extractor>) -> Self {
        self.engine.register_extractor(name, extractor);
        self
    }
    
    /// Build validation engine
    pub fn build(self) -> ValidationEngine {
        self.engine
    }
}

impl Default for ValidationConfigBuilder {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn test_assertion_builder() {
        let builder = AssertionBuilder::new()
            .status_code(200)
            .json_path_equals("body.message", json!("success"))
            .extract_json_path("body.id", "item_id");
        
        let (validators, extractors) = builder.build();
        
        assert!(!validators.is_empty());
        assert!(!extractors.is_empty());
    }

    #[test]
    fn test_common_assertions() {
        let ok_assertion = CommonAssertions::ok();
        let (validators, _) = ok_assertion.build();
        assert!(!validators.is_empty());
        
        let auth_assertion = CommonAssertions::auth_response();
        let (validators, extractors) = auth_assertion.build();
        assert!(!validators.is_empty());
        assert!(!extractors.is_empty());
    }

    #[test]
    fn test_regex_assertion() {
        let builder = AssertionBuilder::new()
            .json_path_regex("body.email", r"^[^@]+@[^@]+\.[^@]+$")
            .unwrap();
        
        let (validators, _) = builder.build();
        assert!(!validators.is_empty());
    }

    #[test]
    fn test_validation_config_builder() {
        let config = ValidationConfigBuilder::new()
            .add_validator("custom".to_string(), Box::new(StatusCodeValidator::new(200)))
            .build();
        
        // The engine should have the custom validator registered
        // This would be tested with actual validation calls
    }
}
